package com.sense.cloud.coreservice.sdk.auth.wrapper;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;



public class BodyHttpServletRequestWrapper extends HttpServletRequestWrapper {
  public static final String UTF8 = "UTF-8";
  public static final Charset UTF8_CHARSET = Charset.forName(UTF8);
  private Map<String, String[]> parameterMap;
  private final byte[] body;

  public byte[] getBody() {
    return body;
  }

  /**
   * @param request.
   * @throws IOException 数据流.
   */
  public BodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    InputStream ins = request.getInputStream();
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    int index = 0;
    byte[] bs = new byte[4096];
    if ( ins != null ) {
      while ((index = ins.read(bs)) > -1) {
        bos.write(bs, 0, index);
      }
    }
    body = bos.toByteArray();
    bos.flush();
    bos.close();
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {
    final ByteArrayInputStream bis = new ByteArrayInputStream(body);
    ServletInputStream sis = new ServletInputStream() {

      @Override
      public int read() throws IOException {
        return bis.read();
      }

      @Override
      public void setReadListener(ReadListener readListener) {
      }

      @Override
      public boolean isReady() {
        return false;
      }

      @Override
      public boolean isFinished() {
        return false;
      }
    };
    return sis;
  }

  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }

  @Override
  public String getParameter(String key) {
    Map<String, String[]> parameterMap = getParameterMap();
    String[] values = parameterMap.get(key);
    return values != null && values.length > 0 ? values[0] : null;
  }

  @Override
  public String[] getParameterValues(String key) {
    Map<String, String[]> parameterMap = getParameterMap();
    return parameterMap.get(key);
  }

  @Override
  public Map<String, String[]> getParameterMap() {
    if (parameterMap == null) {
      Map<String, String[]> result = new LinkedHashMap<String, String[]>();
      decode(getQueryString(), result);
      parameterMap = Collections.unmodifiableMap(result);
    }
    return parameterMap;
  }

  private String[] concatStringArray(String[] src, String element) {
    if (src == null || src.length == 0) {
      src = new String[1];
      src[0] = element;
      return src;
    } else {
      String[] newArr = new String[src.length + 1];
      System.arraycopy(src, 0, newArr, 0, src.length);
      newArr[newArr.length - 1] = element;
      return newArr;
    }
  }

  private void toMap(Iterable<NameValuePair> inputParams, Map<String, String[]> toMap) {
    for (NameValuePair e : inputParams) {
      String key = e.getName();
      String value = e.getValue();
      if (toMap.containsKey(key)) {
        String[] newValue = concatStringArray(toMap.get(key), value);
        toMap.remove(key);
        toMap.put(key, newValue);
      } else {
        toMap.put(key, new String[] { value });
      }
    }
  }

  private void decode(String queryString, Map<String, String[]> result) {
    toMap(decodeParams(queryString), result);
  }

  private Iterable<NameValuePair> decodeParams(String queryString) {
    List<NameValuePair> params = null;
    if (queryString == null || queryString.length() == 0) {
      params = new ArrayList<>();
    } else {
      params = URLEncodedUtils.parse(queryString, UTF8_CHARSET);
    }

    String cts = getContentType();
    if (cts != null) {
      ContentType ct = ContentType.parse(cts);
      if (ct.getMimeType().equals(ContentType.APPLICATION_FORM_URLENCODED.getMimeType())) {
        List<NameValuePair> postParams = URLEncodedUtils.parse(new String(this.body, UTF8_CHARSET),
            UTF8_CHARSET);
        params.addAll(postParams);
      }
    }
    return params;
  }
}
